import axios from "axios";
import os from "os";
import fs from "fs-extra";
import moment from "moment";
import path from "path";
import { toolchain } from "../toolchain.js";
import { Hudson } from "./hudson.js";
import _ from "lodash";
import { md5 } from "./vendor.js";
import { env } from "../env.js";

export const MsgSupporter = 'weixin';

interface IWxUserChatPaylodText {
    touser: string,
    msgtype: 'text',
    text: { content: string },
    safe: 1 | 0
}

interface IWxUserChatPayloadMarkdown {
    touser: string,
    msgtype: 'markdown',
    markdown: { content: string },
    safe: 0
}

type IWxRobotMsgPayload = {
    chatid: string
} & ({
    mention?: string,
    payload: {
        msgtype: 'text',
        text: {
            content: string
        }
    }
} | {
    payload: {
        msgtype: 'markdown',
        markdown: {
            content: string
        }
    }
} | {
    payload: {
        msgtype: 'image',
        image: {
            base64: string
            md5: string
        }
    }
} | {
    payload: {
        msgtype: 'news',
        news: {
            articles: {
                title: string
                description?: string
                url: string
                picurl?: string
            }[]
        }
    }
} | {
    payload: {
        msgtype: 'file',
        file: {
            media_id: string
        }
    }
})

interface IWxAppUploadMedia {
    errcode: number;
    errmsg: string;
    type: 'voice' | 'file';
    media_id: string;
    created_at: string;
}

export const enum EFileErrorType {
    CodeError = 1,
    FileNameError
}

const __fileErrorDescMap = {
    [EFileErrorType.CodeError]: '代码有误，请马上处理！',
    [EFileErrorType.FileNameError]: '文件命名错误，请马上处理！'
}

const __alertVar = { userAlreadyAlert: '' };

export async function alertErrorFile(file: string, type: EFileErrorType, msg?: string): Promise<string> {
    if (!path.isAbsolute(file)) {
        if (file.endsWith('.ts') && !file.startsWith('TsScripts')) file = path.join('TsScripts', file);
        file = path.join(toolchain.params.workSpacePath, file);
    }    
    const info = await toolchain.svnClient.info([file], { showItem: 'last-changed-author' });
    const touser = info.trim();
    let s = __fileErrorDescMap[type] + '\n' + `项目：${toolchain.option.projectName}`;
    if (msg) {
        s += `\n错误信息: ${msg}`;
    }
    await sendUserChatText(touser, encodeURIComponent(s));
    return touser;
}

export async function sendBuildFailureAlert(msg: string, printMsg: boolean = true, suggestRetry = false): Promise<void> {
    if (printMsg) console.error(msg);

    if (msg.length > 512) {
        msg = msg.substring(0, 512) + `\n...more`;
    }

    const now = _.now();
    const timeslapse = now - toolchain.startAt;
    const timeStr = moment(timeslapse).utcOffset(0).format('HH:mm:ss');
    
    if (toolchain.params.channelCfg != null) {
        const chatid = toolchain.params.channelCfg.chatid;
        if (chatid) {
            let content = msg;
            if (__alertVar.userAlreadyAlert) {
                content += `\n已通知 \`@${__alertVar.userAlreadyAlert}\` 进行处理`;
            }
            const groupMsg = `# <font color=\"warning\">版本构建失败</font>
>发起人：@${toolchain.startUser}
>项目：${Hudson.getJobName()} #${Hudson.getBuildNumber()}
>时间：${moment().format(moment.HTML5_FMT.TIME)}
>耗时：${timeStr}
>${content} 
>点击查看日志：[构建日志](http://${toolchain.params.ip}:8080/hudson/job/${Hudson.getJobName()}/${Hudson.getBuildNumber()}/consoleText)`;
        
            // 先群发消息
            await sendRobotMsg('markdown', groupMsg);
            if (suggestRetry) await sendRobotMsg('text', '别紧张，再构一次看看~');
        } else {
            console.log('group chat skipped cause no chatid');
        }
    }

    if (!toolchain.startByTimer && toolchain.startUser) {
        if (__alertVar.userAlreadyAlert.toLowerCase() != toolchain.startUser.toLowerCase()) {
            // 再私发
            if (suggestRetry) msg += '\n请重新构建';
            const userPaylod: IWxUserChatPayloadMarkdown = {
                touser: toolchain.startUser,
                msgtype: 'markdown',
                markdown: {
                    content: `<font color=\"warning\">你发起的版本构建失败了：${Hudson.getJobName()} #${Hudson.getBuildNumber()}</font>
    点击查看日志：[构建日志](http://${toolchain.params.ip}:8080/hudson/job/${Hudson.getJobName()}/${Hudson.getBuildNumber()}/consoleText)
    ${msg}`
                },
                safe: 0,
            };
            await sendUserChat(userPaylod);
        }

        // 有一定几率触发VIP广告
        if (Math.random() > 0.85) {
            await sendVIPAD();
        }
    } else {
        console.log('user chat skipped cause cannot tell authenticated user.');
    }
}

export async function sendBuildSuccess(conclusion: string): Promise<void> {
    const now = _.now();
    const timeslapse = now - toolchain.startAt;
    const timeStr = moment(timeslapse).utcOffset(0).format('HH:mm:ss');

    let msg = '';
    if (conclusion) msg = `>${conclusion}`;

    const otherMsgs = toolchain.buildMessages.join('\n');
    if (otherMsgs) {
        if (msg) msg += '\n';
        msg += otherMsgs;
    }
    if (msg) msg = '\n' + msg;
    if (toolchain.appendLogURL) {
        msg += `
>点击查看日志：[构建日志](http://${toolchain.params.ip}:8080/hudson/job/${Hudson.getJobName()}/${Hudson.getBuildNumber()}/consoleText)`;
    }
    const chatid = toolchain.params.channelCfg?.chatid;
    if (chatid) {
        const groupMsg = `# <font color=\"info\">版本构建成功</font>
>发起人：@${toolchain.startUser}
>项目：${Hudson.getJobName()} #${Hudson.getBuildNumber()}
>时间：${moment().format(moment.HTML5_FMT.TIME)}
>耗时：${timeStr}${msg}`;
    
        // 先群发消息
        await sendRobotMsg('markdown', groupMsg);
    } else {
        console.log('group chat skipped cause no chatid');
    }

    if (!toolchain.startByTimer && toolchain.startUser) {
        // 再私发
        const userPaylod: IWxUserChatPayloadMarkdown = {
            touser: toolchain.startUser,
            msgtype: 'markdown',
            markdown: {
                content: `<font color=\"info\">你发起的版本构建已完成：${Hudson.getJobName()} #${Hudson.getBuildNumber()}</font>${msg}`
            },
            safe: 0,
        };
        await sendUserChat(userPaylod);
    } else {
        console.log('user chat skipped cause cannot tell authenticated user.');
    }
}

export async function sendUserChatText(touser: string, content: string): Promise<void> {
    const payload: IWxUserChatPaylodText = {
        touser,
        msgtype: 'text',
        text: {
            content,
        },
        safe: 1
    };
    await sendUserChat(payload);
}

async function sendUserChat(payload: IWxUserChatPaylodText | IWxUserChatPayloadMarkdown): Promise<void> {
    const result = await axios.post<string>(apiUrl('api/app/usermsg'), { data: payload }, { timeout: 3000, proxy: false }).catch((e) => {});
    if (result?.data !== 'ok') {
        console.error('user chat sent failed:', payload, result);
    } else {
        __alertVar.userAlreadyAlert = payload.touser;
    }
}

export async function sendRobotImage(base64: string, chatid?: string): Promise<void> {
    if (!chatid) {
        if (toolchain.params.channelCfg != null) {
            chatid = toolchain.params.channelCfg.chatid;
        }
    }
    if (chatid) {
        const mch = base64.match(/^data:image\/(\w+);base64,/);
        if (mch == null) {
            console.log('base64 format error!');
            return;
        }
        const ext = mch[1];
        const base64Data = base64.replace(mch[0], '');
        const buffer = Buffer.from(base64Data, 'base64');
        await fs.writeFile(path.join(os.tmpdir(), 'unitybuilderrobotimage.' + ext), buffer);
        let payload: IWxRobotMsgPayload = {
            chatid,
            payload: {
                msgtype: 'image',
                image: {
                    base64: base64Data,
                    md5: md5(buffer)
                }
            }
        }
        await sendRobotMsgInternal(payload);
    } else {
        console.log('robot msg skipped cause no chatid');
    }
}

export async function sendRobotMsg(type: 'text' | 'markdown' | 'file', msg: string, mention?: string, chatid?: string): Promise<void> {
    if (!chatid) {
        if (toolchain.params.channelCfg != null) {
            chatid = toolchain.params.channelCfg.chatid;
        }
    }
    if (chatid) {
        let payload: IWxRobotMsgPayload;
        if (type == 'text') {
            payload = {
                chatid,
                mention,
                payload: {
                    msgtype: 'text',
                    text: {
                        content: msg
                    }
                }
            }
        } else if (type == 'markdown') {
            payload = {
                chatid,
                payload: {
                    msgtype: 'markdown',
                    markdown: {
                        content: msg
                    }
                }
            }
        } else if (type == 'file') {
            payload = {
                chatid,
                payload: {
                    msgtype: 'file',
                    file: {
                        media_id: msg
                    }
                }
            }
        } else {
            console.error('msg type not supported!');
            return;
        }
        await sendRobotMsgInternal(payload);
    } else {
        console.log('robot msg skipped cause no chatid');
    }
}

export async function sendVIPAD(): Promise<void> {
    if (toolchain.params.channelCfg != null) {
        const chatid = toolchain.params.channelCfg.chatid;
        if (chatid) {
            const payload = {
                chatid,
                payload: {
                    msgtype: "news",
                    news: {
                        articles: [
                            {
                                title: "欢迎使用付费咨询业务",
                                description: "点击体验，早扫早享受",
                                url: "http://builder.fygame.com:3000/static/consulingfee.png",
                                picurl: "http://builder.fygame.com:3000/assets/img/vip.jpg"
                            }
                        ]
                    }
                }
            } as IWxRobotMsgPayload;
            await sendRobotMsgInternal(payload);
        }
    }
}

async function sendRobotMsgInternal(payload: IWxRobotMsgPayload): Promise<void> {
    const url = apiUrl('api/app/robotmsg');
    const result = await axios.post<string>(url, { data: payload }, { timeout: 3000, proxy: false }).catch((e) => {
        console.error('Exception when post robot msg:', e);
    });
    if (result?.data !== 'ok') {
        console.error('robot msg sent failed:', payload, result);
    }
}

/**
 * 上传文件
 * @param file 注意空文件上传将失败
 * @returns 
 */
export async function uploadMedia(file: string): Promise<string | null> {
    if (toolchain.params.channelCfg != null) {
        const chatid = toolchain.params.channelCfg.chatid;
        if (chatid) {
            const payload = {
                chatid,
                file
            };

            const result = await axios.post<IWxAppUploadMedia>(apiUrl('api/app/uploadMedia'), payload, { timeout: 3000, proxy: false }).catch((e) => {
                console.error('Exception when post media:', e);
            });
            if (result?.data.errcode !== 0) {
                console.error('upload media failed:', payload, result);
            }
            return result?.data.media_id ?? null;
        }
    }
    return null;
}

function apiUrl(url: string): string {
    let o = env.ChatSvr;
    if (!o.endsWith('/')) {
        o += '/';
    }
    return o + (url.startsWith('/') ? url.substring(1) : url);
}

export function emphasize(str: string): string {
    return `\`${str}\``;
}